﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace HashFoo.Core
{
    /// <summary>
    /// Utility methods for web based functions.
    /// </summary>
    public static class WebUtil
    {
        /// <summary>
        /// Certain headers must be set explicitly.  Do this automagically.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="headers"></param>
        public static void SetHeaders(this HttpWebRequest request, WebHeaderCollection headers)
        {
            var filteredHeaders = new WebHeaderCollection();
            var explicitSetActions = new List<Action<HttpWebRequest>>();

            foreach (var headerKey in headers.AllKeys)
            {
                switch (headerKey)
                {
                    case "Content-Type":
                        explicitSetActions.Add(r => r.ContentType = headers[HttpRequestHeader.ContentType]);
                        break;
                    case "Accept":
                        explicitSetActions.Add(r => r.Accept = headers[HttpRequestHeader.Accept]);
                        break;
                    default:
                        filteredHeaders[headerKey] = headers[headerKey];
                        break;
                }
            }

            request.Headers = filteredHeaders;
            foreach (var action in explicitSetActions) action(request);
        }

        /// <summary>
        /// Executes web functions in a safe try/catch block and handles error callbacks.
        /// </summary>
        /// <param name="webAction">The action that uses the web infrastructure and may throw web based exceptions.</param>
        /// <param name="errorCallback"></param>
        public static void SafeWebExecute(Action webAction, Action<WebRequestError> errorCallback)
        {
            try
            {
                webAction();
            }
            catch (WebException e)
            {
                // if there is no callback for errors, don't do any special handling.
                if (errorCallback == null) throw;

                // get the response of the exception.
                // if the error occured before there was a response, mark as complete and return.
                var response = e.Response as HttpWebResponse;
                if (response == null)
                {
                    errorCallback(new WebRequestError(e));
                    return;
                }

                // we have a response:
                // capture the status, and read the response stream, call the error callback
                var status = response.StatusCode;
                try
                {
                    errorCallback(new WebRequestError(response, e));
                }
                catch (Exception)
                {
                    // oh man, its getting bad:
                    // even though we had a response, we still errored out.  Mark as complete and call the error callback.
                    errorCallback(new WebRequestError(status, String.Empty, e));
                }
                finally
                {
                    response.Dispose();
                }
            }
            catch (Exception e)
            {
                // we have some sort of crappy issue:
                // if there is no callback for errors, don't do any special handling, else, call the error callback.
                if (errorCallback == null) throw;

                // else
                errorCallback(new WebRequestError(e));
            }
        }

        /// <summary>
        /// Converts an <see cref="HttpWebResponse"/> stream to a string.
        /// </summary>
        /// <param name="response"></param>
        /// <returns></returns>
        /// <exception cref="WebException">Any web based exception thrown while trying to read the response stream.</exception>
        public static string GetResponseAsString(HttpWebResponse response)
        {
            using(var responseStream = response.GetResponseStream())
            using (var reader = new StreamReader(responseStream))
            {
                var responseData = reader.ReadToEnd();
                return responseData;
            }
        }

        /// <summary>
        /// Creates a http basic authentication header value from a user name and password.
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="userPassword"></param>
        /// <returns></returns>
        public static string CreateHttpBasicAuthHeader(string userName, string userPassword)
        {
            var authInfo = userName + ":" + userPassword;
            authInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(authInfo));
            return "Basic " + authInfo;
        }
    }
}